package com.ding.manager.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.interfaces.Claim;
import com.common.base.dao.redis.RedisDao;
import com.ding.manager.config.DTConstant;
import com.ding.manager.util.*;
import com.ding.manager.vo.DTUser;
import com.dingtalk.api.response.OapiSnsGetuserinfoResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;


@WebFilter(filterName = "sessionFilter", urlPatterns = "/*")
public class DTSessionFilter implements Filter {

	static Logger log = Logger.getLogger(DTSessionFilter.class);

	/**
	 * 用户登录是否进行参数加密 0:不加密
	 */
	public static final int APP_PARAM_ISENCRYPT_0 = 0;
	/**
	 * 用户登录是否进行参数加密 1:加密
	 */
	public static final int APP_PARAM_ISENCRYPT_1 = 1;

	/**
	 * 保存登录地址信息的Session键值
	 */
	private static final String FILTER_REQUEST = "@@filter_request";

	/**
	 * 用户对象存储redis的路径
	 */
	public static final String USERREDISKEY = "account:dt:";

	/**
	 * appId存储redis的路径
	 */
	public static final String USERAPPIDKEY = "account:appId:";

	/**
	 * 不进行登录验证的url
	 */
	private static String[] excludeUrls = null;

	/**
	 * 不进行登录验证的url资源
	 */
	private static String[] excludeStaticFiles = null;

	/**
	 * Token生成时无需校验既可长时间访问的的appId字符串集合,时长为10天;(其它appId为60分钟)
	 */
	public static List<String> tokenNoValidAppIdLst = null;
	/**
	 * Token失效时间:60分钟(60*60*1000)
	 */
	public static Long tokenExpTimeHour = null;
	/**
	 * Token失效时间:10天(10*24*60*60*1000)
	 */
	public static Long tokenExpTimeDay = null;

	/**
	 * redis对象
	 */
	private static RedisDao redisDao;

	/**
	 * basedao
	 */
//	private static BaseDao baseDao;

	@Override
	public void init(FilterConfig config) throws ServletException {

		ServletContext servletContext = config.getServletContext();
		WebApplicationContext wct = WebApplicationContextUtils.getWebApplicationContext(servletContext);
		wct.getBean("commonConfigUtil");

		String appExcludeUrls = CommonConfigUtil.getProperty("appExcludeUrls");

		String appExcludeStaticFiles = CommonConfigUtil.getProperty("appExcludeStaticFiles");

		if (StringUtils.isNotEmpty(appExcludeUrls)) {
			excludeUrls = appExcludeUrls.split(",");
		}

		if (StringUtils.isNotEmpty(appExcludeStaticFiles)) {
			excludeStaticFiles = appExcludeStaticFiles.split(",");
		}

		String commonTokenNoValidAppId = CommonConfigUtil.getProperty("token.noValid.appId");
		if (StringUtils.isNotEmpty(commonTokenNoValidAppId)) {
			tokenNoValidAppIdLst = Arrays.asList(commonTokenNoValidAppId.split(","));
		} else {
			tokenNoValidAppIdLst = new ArrayList<>();
		}

		String commonTokenExpTimeHour = CommonConfigUtil.getProperty("token.expTime.hour");
		if (StringUtils.isNotEmpty(commonTokenExpTimeHour)) {
			tokenExpTimeHour = Long.valueOf(commonTokenExpTimeHour);
		}

		String commonTokenExpTimeDay = CommonConfigUtil.getProperty("token.expTime.day");
		if (StringUtils.isNotEmpty(commonTokenExpTimeDay)) {
			tokenExpTimeDay = Long.valueOf(commonTokenExpTimeDay);
		}

		if (redisDao == null) {
			redisDao = (RedisDao) wct.getBean("redisDao");
		}
//		if (baseDao == null) {
//			baseDao = (BaseDao) wct.getBean("baseDao");
//		}

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		httpResponse.setHeader("Access-Control-Allow-Origin", "*");
		String requestUrl = httpRequest.getRequestURI();
		// 默认设置当前的加密状态为不加密状态;
		int isEncrypt = APP_PARAM_ISENCRYPT_0;
		// 保证该过滤器在一次请求中只被调用一次
		if (request != null && request.getAttribute(FILTER_REQUEST) != null) {
			chain.doFilter(request, response);
		} else {
			// 设置过滤标识，防止一次请求多次过滤
			request.setAttribute(FILTER_REQUEST, Boolean.TRUE);
			// 过滤掉被设置的地址和文件;
			if (!this.isExcludeUrl(requestUrl) && !this.isExcludeStaticFile(requestUrl, httpRequest)) {
				// 如果当前访问请求没有被过滤掉,那么说明当前访问地址都是需要传递publicKey的;
				String publicKey = httpRequest.getHeader("publicKey");
				if (StringUtils.isEmpty(publicKey)) {
					// returnData(httpResponse,"header中的参数{publicKey}为空,无法访问!");
					returnData(httpResponse, ConstantMsgUtil.getSessionTimeOutStatus(),
							ConstantMsgUtil.getSessionTimeOutMsg());
					return;
				}
				// 如果当前appId对应的isEncrypt=1,说明当前参数需要加密;
				if (isEncrypt == APP_PARAM_ISENCRYPT_1) {
					try {
						PageData paramPd = new PageData(httpRequest);
						String param = paramPd.getString("param");
						// 如果当前参数中的param为空,说明当前没有参数传递;
						if (StringUtils.isEmpty(param)) {
							// 如果param为空,但是参数中还存在别的属性,说明当前属性未加密
							if (paramPd.keySet().size() > 0) {
								returnData(httpResponse, "参数{param}未加密,无法访问!");
								return;
							}
						} else {
							// 如果param不为空,但是参数中除了param还存在别的属性,说明当前参数非法传递;
							if (paramPd.keySet().size() > 1) {
								returnData(httpResponse, "参数{param}非法传递,无法访问!");
								return;
							}
							param = RSAUtils.getDecode(param);
							String[] paramLst = param.split("&");
							for (String filedStr : paramLst) {
								String[] fileds = filedStr.split("=");
								paramPd.put(fileds[0], fileds[1]);
							}
						}
					} catch (Exception e) {
						returnData(httpResponse, "参数{param}解密失败,无法访问!");
						return;
					}
				}
				DTUser dtUser = getDTUser(httpRequest);
				if (dtUser == null) {
					// 用户信息为空,说明当前用户信息已经失效,需要重新登录;
					returnData(httpResponse, ConstantMsgUtil.getSessionTimeOutStatus(),
							ConstantMsgUtil.getSessionTimeOutMsg());
					return;
				} else {
					// 用户信息不为空,但是publicKey为空,说明当前用户已经在别的地方登录;
					if (StringUtils.isEmpty(dtUser.getPublicKey())) {
						returnData(httpResponse, ConstantMsgUtil.getAccountControlStatus(),
								ConstantMsgUtil.getAccountControlMsg());
						return;
					}
				}
			}
			chain.doFilter(request, response);
		}
	}

	@Override
	public void destroy() {

	}

	/**
	 * Description:
	 *
	 * @auther: xumy
	 * @date: 2018/11/13 14:40
	 */
	public static DTUser saveTokenCmnUser(HttpServletRequest request, OapiSnsGetuserinfoResponse.UserInfo userInfo) throws Exception {
		if (userInfo == null) {
			throw new Exception("用户信息为空,保存进Token失败!!!");
		}

		String unionId = userInfo.getUnionid();
		String nickName = userInfo.getNick();

		String appId = request.getHeader("appId");
		String publicKey = request.getHeader("publicKey");
		if (StringUtils.isNotEmpty(publicKey)) {
			redisDao.delete(USERREDISKEY + publicKey);
		}
		// 如果当前appId在文件中进行配置,则说明该appId不需要进行常规超时设置;
		Long tokenExpTime = tokenExpTimeHour;
//		if (tokenNoValidAppIdLst.contains(appId)) {
//			tokenExpTime = tokenExpTimeDay;
//		}
		publicKey = JwtTokenUtil.createToken(unionId, nickName, tokenExpTime.intValue());
		DTUser dtUser = new DTUser();

		String duserId = getUserIdByUnionId(unionId);
		if(StringUtils.isEmpty(duserId)){
			return null;
		}else {
			dtUser.setdUserId(duserId);
			dtUser.setUnionid(unionId);
			dtUser.setNick(nickName);
			dtUser.setPublicKey(publicKey);
			dtUser.setAppId(appId);
			dtUser.setTokenExpTime(tokenExpTime);

            //获取user dao详情
//            int userId = (Integer) baseDao.findForObject("DITUserMapper.getUserIdByD",dtUser.getdUserId());
            int userId = 1;
            dtUser.setUserId(userId);
//            String deptName = (String)baseDao.findForObject("DITUserMapper.getUserDeptName",userId);
            String deptName = "徐某人";
            if(StringUtils.isNotEmpty(deptName)) {
                dtUser.setDeptName(deptName);
            }

			redisDao.vSet(USERREDISKEY + unionId, dtUser, tokenExpTime);
			redisDao.vSet(USERREDISKEY + publicKey, publicKey, tokenExpTime);
			return dtUser;
		}
	}

	/**
	 * 根据token获取CmnUser对象
	 * 
	 * @param request
	 */
	public static DTUser getDTUser(HttpServletRequest request) {
		DTUser dtUser = null;
		try {
			if (redisDao == null) {
				WebApplicationContext wct = WebApplicationContextUtils
						.getWebApplicationContext(request.getSession().getServletContext());
				redisDao = (RedisDao) wct.getBean("redisDao");
			}
			String paramPublicKey = request.getHeader("publicKey");
			// 如果paramPublicKey为空,直接返回空对象;
			if (StringUtils.isEmpty(paramPublicKey)) {
				return dtUser;
			}
			Map<String, Claim> claimMap = JwtTokenUtil.verifyToken(paramPublicKey);
			// 如果根据token获取用户信息不为空,说明当前token未过期;
			if (claimMap != null) {
				String unionid = claimMap.get("unionid").asString();
				String appId = request.getHeader("appId");
				// 根据userId获取Reids中保存的用户信息;
				Object object = redisDao.vGet(USERREDISKEY + unionid);
				// 如果用户信息为空,说明当前用户信息已经失效;
				if (object != null) {
					dtUser = (DTUser) object;
					// 获取用户信息中的token值;
					String newPublicKey = dtUser.getPublicKey();
					// 根据token(key)获取该token对应的token(val)
					Object oldPublicKey = redisDao.vGet(USERREDISKEY + newPublicKey);
					// 如果paramPublicKey和newPublicKey不匹配,并且和oldPublicKey不匹配,说明paramPublicKey已经失效;
					if (!paramPublicKey.equals(newPublicKey) && !paramPublicKey.equals(oldPublicKey)) {
						// paramPublicKey失效两种可能[1:长时间不操作失效;2:同一账号在别的地方被登录;]
						Object publicKeyObj = redisDao.vGet(USERREDISKEY + paramPublicKey);
						// 根据paramPublicKey从Redis获取数据如果为空,说明当前账号超时未操作,直接返回空值;否则就是账号被多次登录;
						if (publicKeyObj == null || "".equals(publicKeyObj)) {
							dtUser = null;
						} else {
							dtUser.setPublicKey("");
						}
					} else {
						return dtUser;
					}
				}
			}
			redisDao.delete(USERREDISKEY + paramPublicKey);



		} catch (Exception e) {
			log.error("根据Token从Redis中获取用户信息失败!!!", e);
		}
		return dtUser;
	}

	/**
	 * 清除Session信息
	 * 
	 * @param request
	 */
	public static void removeTokenDTUser(HttpServletRequest request) {
		try {
			if (redisDao == null) {
				WebApplicationContext wct = WebApplicationContextUtils
						.getWebApplicationContext(request.getSession().getServletContext());
				redisDao = (RedisDao) wct.getBean("redisDao");
			}
			String publicKey = request.getHeader("publicKey");
			Map<String, Claim> claimMap = JwtTokenUtil.verifyToken(publicKey);
			if (claimMap != null) {
				String unionid = claimMap.get("unionid").asString();
				String appId = request.getHeader("appId");
				redisDao.delete(USERREDISKEY + unionid);
			}
			redisDao.delete(USERREDISKEY + publicKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 判断请求url是否为过滤的
	 * 
	 * @param requestUrl
	 * @return boolean
	 */
	private boolean isExcludeUrl(String requestUrl) {
		for (String excludeUrl : excludeUrls) {
			if (requestUrl.indexOf(excludeUrl.trim()) > -1) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 当前URI资源是否需要登录才能访问
	 * 
	 * @param requestURI
	 * @param request
	 * @return
	 */
	private boolean isExcludeStaticFile(String requestURI, HttpServletRequest request) {
		if (request.getContextPath().equalsIgnoreCase(requestURI)
				|| (request.getContextPath() + "/").equalsIgnoreCase(requestURI)) {
			return true;
		}
		for (String uri : excludeStaticFiles) {
			if (requestURI != null && requestURI.indexOf(uri) >= 0) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 返回数据结果
	 * 
	 * @param httpResponse
	 * @param msg:提示信息
	 * @return
	 */
	private void returnData(HttpServletResponse httpResponse, String msg) {
		returnData(httpResponse, ConstantMsgUtil.getFailStatus(), msg);
	}

	private void returnData(HttpServletResponse httpResponse, int status, String msg) {
		try {
			ResponseEntity responseEntity = new ResponseEntity();
			responseEntity.setStatus(status);
			responseEntity.setMsg(msg);
			httpResponse.setCharacterEncoding("UTF-8");
			httpResponse.setContentType("application/json; charset=utf-8");
			httpResponse.setDateHeader("Expires", 0); // 在代理服务器端防止缓冲
			PrintWriter out = httpResponse.getWriter();
			out.print(JSONObject.toJSONString(responseEntity));// session失效
			out.flush();
		} catch (Exception e) {
			log.error("返回数据失败,系统异常!!!", e);
			System.exit(1);
		}
	}



	public static String getUserIdByUnionId(String unionid){
		String result = HttpUtils.sendGet(DTConstant.GETUSERIDBYUNIONID_URL,"access_token="+DTConstant.ACCESS_TOKEN+"&unionid="+unionid);
		JSONObject jsonObject = new JSONObject();
		jsonObject = JSON.parseObject(result);
		Integer errcode = jsonObject.getInteger("errcode");
		if(DTConstant.resCode.intValue() == errcode ){
			return jsonObject.getString("userid");
		}else {
			return null;
		}
	}


}
